#!/usr/bin/env python

# Copyright 2012 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Handler for issuing tokens to Relying Parties."""

import base64
import hashlib
import json
import logging
import time
from base import BaseHandler
from google.appengine.api import app_identity
from lib import authz, utils
from lib import http
from lib.accept_content import accept_content
from lib.loggable import loggable
import model

__author__ = 'Maciej Machulak'
__maintainer__ = 'Maciej Machulak'
__email__ = 'mmachulak@google.com'

__copyright__ = 'Copyright 2012, Google Inc.'
__license__ = 'Apache License 2.0'
__version__ = '0.2'
__status__ = 'Prototype'

SIG_ALG = 'RS256'


class AttributeTokenHandler(BaseHandler):
  """Handles requests to issue tokens to RPs for specific attributes."""

  @authz.api_authz_required(
    scopes=['https://www.googleapis.com/auth/streetidentity.read',
            'https://www.googleapis.com/auth/streetidentity.write'])
  @accept_content('application/x-www-form-urlencoded')
  @loggable('issue tokens for attributes')
  def post(self):
    """Handles POST requests and issues tokens to RPs."""

    application = self.application

    id_ = self.request.get('id')
    provider = self.request.get('provider')

    logging.debug(
      'received request for token for attribute with id: %s', id_)

    # Getting attribute from DB
    attribute = self._get_attribute_for_user(id_, self.user, provider)

    if attribute is None:
      logging.debug('no attribute with the provided id for the user')
      self.response.set_status(403)
      return

    logging.debug('successfully retrieved attribute - generating token')

    # Generate the token
    now = time.time()
    now_before = now
    logging.debug('now before: %s', now_before)
    now_after = now + 60 * 15
    logging.debug('now after: %s', now_after)
    application_id = app_identity.get_service_account_name()
    logging.debug('application id: %s', application_id)
    token = self._buildjwt(application_id, application.client_id,
      attribute.id, now_before, now_after)
    logging.debug('token: %s', token)  # TODO(mmachulak) remove token logging!

    # Store the token within the datastore
    authorization = model.Authorization(
      application=application,
      attribute=attribute,
      iat=int(now_before),
      exp=int(now_after),
      token_hash = utils.get_token_hash(token)
    )
    authorization.put()
    logging.debug('storing authorization within the database')

    # Prepare the response
    response = {'token': token}

    self.response.set_status(200)
    self.response.headers[http.Header.content_type] =\
    'application/json; charset=utf-8'
    self.response.out.write(json.dumps(response))
    return

  def _get_attribute_for_user(self, id_, user, provider):
    return model.Attribute.all()\
    .filter('user = ', user)\
    .filter('id = ', id_)\
    .filter('provider = ', provider)\
    .get()

  def _buildjwt(self, issuer, audience, resource, now_before, now_after):
    logging.debug('building signed jwt')
    logging.debug('issuer: %s', issuer)
    logging.debug('audience: %s', audience)
    logging.debug('resource: %s', resource)
    logging.debug('issued_at: %s', str(now_before))
    logging.debug('expires: %s', str(now_after))
    jsonpayload = ('{\"iss\":\"'
                   + issuer
                   + '\"aud\":\"'
                   + audience
                   + '\",\"scope\":\"'
                   + resource
                   + '\",\"iat\":'
                   + str(now_before)
                   + ',\"exp\":'
                   + str(now_after)
                   + '}'
      )

    logging.debug(jsonpayload)

    header = ('{\"alg\":\"'
              + SIG_ALG
              + '\",\"kid\":\"'
              + ''
              + '\"}'
      )

    logging.debug(header)

    basesignaturestring = (base64.urlsafe_b64encode(header)
                           + '.'
                           + base64.urlsafe_b64encode(jsonpayload)
      )
    result = app_identity.sign_blob(basesignaturestring)
    keyid = result[0]
    sig = result[1]
    signedjsontoken = (basesignaturestring
                       + '.'
                       + base64.urlsafe_b64encode(sig))
    return signedjsontoken
